package com.api.base.config.auth;

import com.api.base.config.auth.service.DetailsServic;
import com.api.common.JSONUtils;
import com.api.core.ServiceException;
import com.api.core.response.Result;
import com.api.core.response.ResultEnum;
import com.api.core.response.ResultGenerator;
import io.jsonwebtoken.ExpiredJwtException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    public static final String WS_TOKEN_HEADER = "Sec-WebSocket-Protocol";

    @Value("${jwt.header}")
    private String tokenHeader;
    @Value("${jwt.tokenHead}")
    private String tokenHead;

    private DetailsServic userDetailsService;
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    public JwtAuthenticationTokenFilter(DetailsServic userDetailsService, JwtTokenUtil jwtTokenUtil) {
        this.userDetailsService = userDetailsService;
        this.jwtTokenUtil = jwtTokenUtil;

    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException {
        try {
            String authHeader = request.getHeader(tokenHeader);
            if (StringUtils.isEmpty(authHeader)) {
                authHeader = request.getHeader(WS_TOKEN_HEADER);
            }
            if (authHeader != null && authHeader.startsWith(tokenHead)) {
                String authToken = authHeader.substring(tokenHead.length());
                String username = jwtTokenUtil.getUsernameFromToken(authToken);
                int type = jwtTokenUtil.getTypeFromToken(authToken);
                if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                    Map<String,Object> params = new HashMap<>();
                    params.put("u",username);
                    params.put("t",type);
                    UserDetails userDetails = userDetailsService.loadUserByUsername(JSONUtils.obj2json(params));
                    if (jwtTokenUtil.validateToken(authToken, username)) {
                        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                        SecurityContextHolder.getContext().setAuthentication(authentication);
                    }
                }
            }
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            logger.error("TokenFilterException", e);
            response.setHeader("Content-Type", "application/json;charset=utf-8");
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            Result result = ResultGenerator.genExceptionResult();
            if (e instanceof RedisConnectionFailureException) {
                result = ResultGenerator.genResult(ResultEnum.REDIS_CONNECTION_FAILUR);
            }
            if(e instanceof ExpiredJwtException){
                result =ResultGenerator.genFailResult(401,"token expired");
            }

            if(e instanceof UsernameNotFoundException){
                result =ResultGenerator.genFailResult(401,e.getMessage());
            }
            if(e instanceof ServiceException){
                result =ResultGenerator.genFailResult(401,e.getMessage());
            }
            response.getWriter().write(JSONUtils.obj2json(result));
            response.getWriter().flush();
        }
    }


}
